// automatic generation from syntax definition AL 

header {
package com.cea.papyrus.classdiagram.parsers.antlr;

import org.eclipse.uml2.uml.*;
import java.util.Iterator;
import java.util.Collection;
import java.util.Vector;

import com.cea.papyrus.classdiagram.parsers.util.Assert;

import com.cea.papyrus.umlutils.Parameter;
}

class OperationLabelParser extends Parser;

options {
	k = 2;
	buildAST = false;
	defaultErrorHandler = false;
}


{
	public OperationLabelParser(TokenStream lexer, Operation operation) {
		this(lexer);
		this.operation = operation;	
		this.contextPackage = new com.cea.papyrus.umlutils.Package(operation.getClass_().getPackage());
	}
	
	/**
	 * Validation state (true = validation : does not modify the operation
	 */
	private boolean isValidation = false;
	
	/**
	 * @return the isValidation boolean
	 */
	public boolean isValidation() {
		return isValidation;
	}

	/**
	 * @param isValidation the isValidation to set (true -> do not modify the operation)
	 */
	public void setValidation(boolean isValidation) {
		this.isValidation = isValidation;
	}
	
	/**
	 * Modified Operation 
	 */
	private Operation operation;
	
	/**
	 * @return the operation
	 */
	public Operation getOperation() {
		return operation;
	}

	/**
	 * @param operation the operation to set
	 */
	public void setOperation(Operation operation) {
		this.operation = operation;
	}
    	
    /** debug mode */
    protected final boolean debugMode = false;  	
    	
    /**
     * Debug method (~System.out.println)
     */
    private void debug(Object debug) {
    	if(debugMode) {
    		System.out.println(debug);	
    	}	
    }
    
    /**
     * Error method (~System.err.println)
     */
    private void error(Object debug) {
    	if(debugMode) {
    		System.err.println(debug);	
    	}	
    }
    
    // all possible modified elements on operation (with default values : necessary ?!)
  	private String name;
    private VisibilityKind visibility;
    private org.eclipse.uml2.uml.Parameter returnParameter;
    private Collection<org.eclipse.uml2.uml.Parameter> parameters = new Vector<org.eclipse.uml2.uml.Parameter>();
    private Collection<org.eclipse.uml2.uml.Operation> redefinedOperations = new Vector<org.eclipse.uml2.uml.Operation>();
    private boolean isQuery = false;
    private boolean isUnique;
    private Collection<org.eclipse.uml2.uml.Constraint> constraints = new Vector<org.eclipse.uml2.uml.Constraint>();
    
    private com.cea.papyrus.umlutils.Package contextPackage = null;
    
	/**
	 * Apply values to the operation. <BR> 
	 * Modifications can not be applied directly. The parser must wait the end, 
	 * until it reaches the end of the label. If the end is reached without any error,
	 * new values can be applied to the target operation.
	 */
	@SuppressWarnings("unchecked")
	private void applyValues() {
		if(!isValidation){
			debug("\nvalues applied :");
			debug("+ name: "+name);
			debug("+ visibility: "+visibility);
			
			debug("+ parameters: "+parameters);
			debug("+ returnParameter: "+returnParameter);
			
			debug("+ isQuery: "+isQuery);
			debug("+ redefinedOperations: "+redefinedOperations);
			
			// setting values
			operation.setName(name);
			operation.setVisibility(visibility);
			
			// parameters = cllection of parameters AND return parameter, if exists
			operation.getOwnedParameters().clear();
			operation.getOwnedParameters().addAll(parameters);
			if(returnParameter != null) {
				operation.getOwnedParameters().add(returnParameter);
			}
			
			// is the operation a Query ?
			operation.setIsQuery(isQuery);
			
			// operation redefinition
			operation.getRedefinedOperations().clear();
			operation.getRedefinedOperations().addAll(redefinedOperations);
			
		} 
	}
	
	/**
	 * Find a type given its name and a context to find it.
	 * @param name the name of the type
	 * @return the type found or <code>null</code> if the element was not found.
	 */
	private Type findTypeByName(String name, org.eclipse.uml2.uml.Element context) throws RecognitionException {
		Type type = null;
		int numTypes = 0;
		org.eclipse.uml2.uml.Package contextPackage = context.getNearestPackage();
		
		// seek the package in which the element should be found...
		Iterator it = contextPackage.getMembers().iterator();
		while( it.hasNext() ) {
			Object tmp = it.next();
			if( tmp instanceof Type ) {
				Type element = ( Type )tmp;
				if(  element.getName().equals(name)  ) {
					numTypes++;
					type = element;
				}
			}
		}
		if(type == null) {// type not found in default package. search in imports.
			it = contextPackage.getImportedMembers().iterator();
			while( it.hasNext() ) {
				Object tmp = it.next();
				if( tmp instanceof Type ) {
					Type element = ( Type )tmp;
					if(  element.getName().equals(name)  ) {
						type = element;
						numTypes++;
					}
				}
			}
		}
		
		if(numTypes == 0) {
			throw new RecognitionException("Type "+name+" not found for operation "+operation.getName());
		} else if(numTypes > 1) {
			debug("There are "+numTypes+" types with the same name....");
		}
		
		return type;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////
// Rules 
//////////////////////////////////////////////////////////////////////////////////////////
label
	:
	visibility name parameterList
	{
		applyValues();
	}
	;
	
visibility
	:
	(	
		PLUS	{ visibility = VisibilityKind.PUBLIC_LITERAL; }
	|	MINUS	{ visibility = VisibilityKind.PRIVATE_LITERAL;}
	|	SHARP	{ visibility = VisibilityKind.PROTECTED_LITERAL;}
	|	TILDE	{ visibility = VisibilityKind.PACKAGE_LITERAL;}
	|			{ visibility = VisibilityKind.PUBLIC_LITERAL; /*default visibility = public */  }
	)
	;

name
	:
	id:IDENTIFIER	
	{ 
		name = id.getText();
	}
	;

parameterList 
	:
	LPAREN
	(
		parameter
		(
			COMMA parameter
		)*
	)?
	RPAREN	
	;
	
parameter 
	:
	{		
		// create a new Parameter
		com.cea.papyrus.umlutils.Parameter currentParameter = new com.cea.papyrus.umlutils.Parameter(UMLFactory.eINSTANCE.createParameter());
	}
	(
		direction[currentParameter]
	)?
	parameterName[currentParameter]
	COLON 
	parameterType[currentParameter]
	(
		multiplicity[currentParameter]
	)?
	{
		parameters.add(currentParameter.getUml2Parameter());
	}
	;
	
direction [ com.cea.papyrus.umlutils.Parameter parameter ]
	:
	{
		Assert.isNotNull(parameter);
	}
	(	"in"		{ parameter.getUml2Parameter().setDirection(ParameterDirectionKind.IN_LITERAL); }
	|	"out"		{ parameter.getUml2Parameter().setDirection(ParameterDirectionKind.OUT_LITERAL); }
	|	"inout"		{ parameter.getUml2Parameter().setDirection(ParameterDirectionKind.INOUT_LITERAL); }
	)
	;

parameterName [ com.cea.papyrus.umlutils.Parameter parameter ]
	:
	{
		Assert.isNotNull(parameter);
	}
	id:IDENTIFIER	
	{ 
		parameter.getUml2Parameter().setName(id.getText());
	}
	;
	
parameterType [ com.cea.papyrus.umlutils.Parameter parameter ]
	:
	{
		Assert.isNotNull(parameter);
	}
	(
		id:IDENTIFIER
		{ 
			// find Type by name
			com.cea.papyrus.umlutils.Type type = contextPackage.findTypeByName(id.getText());
			if(type != null) {
				parameter.getUml2Parameter().setType(type.getUml2Type());
			}
			else {
				throw new RecognitionException("type "+id.getText()+" not found in package "+contextPackage.getName());
			}
		}
	)
	|
	(
		LT "Undefined" GT
	)
	;
	
multiplicity [ com.cea.papyrus.umlutils.Parameter parameter ]
	:
	{
		Assert.isNotNull(parameter);
		int lower = 0;
		int upper = 0;
	}
	LSQUARE
	// { context = IContext.IN_MULTIPLICITY; }
	(
		lower=lowerMultiplicity POINT_POINT upper=upperMultiplicity {
			if(lower > upper && upper != -1) {
				throw new RecognitionException("Lower bound ("+lower+") is greater than upper bound ("+upper+")");
			}
			parameter.getUml2Parameter().setLower(lower);
			parameter.getUml2Parameter().setUpper(upper);
		}
	|	upper=upperMultiplicity {
			if(upper == -1) {
				parameter.getUml2Parameter().setLower(0);
				parameter.getUml2Parameter().setUpper(upper);
			} else {
				parameter.getUml2Parameter().setLower(upper);
				parameter.getUml2Parameter().setUpper(upper);
			}
		}
	)
	RSQUARE
	;
	
lowerMultiplicity returns [int lower = 0]
	:
	in:INTEGER
	{
		lower = Integer.parseInt(in.getText());
	}
	;

upperMultiplicity returns [int upper = 0]
	:
	upper = unlimitedNatural
	;
	
unlimitedNatural returns [int value = 0 ]
	:
	(	STAR			{ value = -1; }
	|	in:INTEGER		{ value = Integer.parseInt(in.getText()); }
	)
	;
		


//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

/* #################################################
   ###                  LEXER                    ###
   #################################################
*/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

class OperationLabelLexer extends Lexer;

options {
	k = 7;
	charVocabulary = '\0' .. '\u00FF';
}

// Definition of newlines
NL	
	:	(	'\r' '\n' 
		|	'\n' '\r'  //Improbable
		|	'\r'
		|	'\n'
		)
	{ $setType(Token.SKIP); newline();}
	;


// White spaces
WS
	:	(	' '
		|	'\t'   
		)
	{ $setType(Token.SKIP); }
	;

QUESTION_MARK
	:	'?'
	;
	
LNOT
	:	'!'
	;
		
DOUBLE_QUOTE
	:	'\"'
	;

SINGLE_QUOTE	
	:	'\''
	;

LPAREN
	:	'('
	;

RPAREN
	:	')'
	;

LSQUARE
	:	'['
	;

RSQUARE
	:	']'
	;

LCURLY
	:	'{'
	;

DOUBLE_LCURLY
	:	"{{"
	;

RCURLY
	:	'}'
	;
	
DOUBLE_RCURLY
	:	"}}"
	;
	
LOGICAL_AND
	:	'&'
	;

DOUBLE_LOGICAL_AND
	:	"&&"
	;

PIPE
	:	'|'
	;

DOUBLE_PIPE
	:	"||"
	;

POINT
	:	'.'
	;

POINT_POINT
	:	".."
	;

COMMA
	:	','
	;

SEMI
	:	';'
	;

SHARP
	:	'#'
	;

TILDE
	:	'~'
	;

COLON
	:	':'
	;

DOUBLE_COLON
	:	"::"
	;

RULE_ASSIGN
	:	"::="
	;

ARROW
	:	"->"
	;

STAR
	:	'*'
	;

DIV
	:	'/'
	;

PLUS
	:	'+'
	;

MINUS
	:	'-'
	;

EQ
	:	"="
	;


ISEQ
	:	"=="
	;

ISNE
	:	"!="
	;

LT
	:	'<'
	;

DOUBLE_LT
	:	"<<"
	;

LE
	:	"<="
	;

GT
	:	'>'
	;

DOUBLE_GT
	:	">>"
	;

GE
	:	">="
	;


INCR
	:	"++"
	;

DECR
	:	"--"
	;

CALLOPERATION
	:	":="
	;

protected
DIGIT
	:	'0'..'9'
	;

protected
ALPHA
	:	'a'..'z' 
	|	'A'..'Z'
	;

protected
UNDERSCORE
	:	'_'
	;

protected
INTEGER
	:	(DIGIT)+
	;

protected
REAL
	:	INTEGER '.' INTEGER
	;

REAL_OR_INT
	:	(INTEGER '.' ~('.')) => REAL 	{$setType(REAL);}
	|	INTEGER			{$setType(INTEGER);}
	;

IDENTIFIER
	:	(ALPHA|UNDERSCORE)(ALPHA|DIGIT|UNDERSCORE)*
	;
	
	
